package com.zp.demo.repush.listener;

import com.zp.demo.repush.RePushUtils;
import com.zp.demo.repush.enums.RePushType;
import com.zp.demo.repush.service.DoRePushExcuteService;
import com.zp.demo.repush.service.func.ReSubmitResultFunc;
import com.zp.demo.repush.vo.RePushProperties;
import com.zp.demo.repush.vo.RePushStrategyEntity;
import com.zp.demo.repush.event.RepayPlanEvent;
import com.zp.demo.repush.service.defined.DefindRePushDbService;
import com.zp.demo.repush.service.defined.DefinedRePushFitter;
import com.zp.demo.repush.vo.BaseRePushVo;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.util.concurrent.ConcurrentMap;


/**
 * 即可推送
 * @Author 张鹏
 * @Date 2018/12/29 16:57
 */
@Component
@Log4j2
public class RePush4LocalListener implements ApplicationListener<RepayPlanEvent> {
    @Autowired
    private DefindRePushDbService rePushDbService;
    @Autowired
    private DefinedRePushFitter rePushFitter;
    @Autowired
    private DoRePushExcuteService doRePushExcuteService;
    @Autowired
    private RePushProperties rePushProperties;
    @Autowired
    private ConcurrentMap<String,String> lockMap;
    // 由于本身为单线程，并发小，无需加锁原子性，所以直接用int类型
    @Override
    public void onApplicationEvent(RepayPlanEvent event) {
        BaseRePushVo baseRePushVo = event.getRePushVo();
        // 获取策略配置信息
        RePushStrategyEntity strategyInfo = doRePushExcuteService.getStrategyProperties(baseRePushVo.getStrategyKey());
       // RePushStrategyEntity rePushStrategyEntity = baseStrategy;
        // 及时推送
        if(strategyInfo.getJsCounts() > 0){
            Object obj = rePush4Return(strategyInfo,baseRePushVo);
            if(obj != null){
                baseRePushVo.setOldResult(obj);
            }
            // 返回结果如果正确就退出
            if(RePushUtils.isSuccess(obj,baseRePushVo)){
                log.info("及时推送结果正确");
                return;
            }
        }
        // 延时推送
        if(strategyInfo.getCounts() > 0){
            new Thread(()->{
                this.rePush4Ys(strategyInfo,baseRePushVo);
            }).start();
        }else{
            lockMap.remove(baseRePushVo.getOnlyOnceKey());
        }
        // 定时推送
        this.addScheduled(baseRePushVo,strategyInfo,false);
    }

    /**
     * 过滤操作
     * @param key
     * @param baseRePushVo
     * @return
     */
    private boolean isFillter(String key,BaseRePushVo baseRePushVo){
        if(!baseRePushVo.getOnlyOnce()){
            return true;
        }
        boolean isFillter =  rePushFitter.checkSuccessOnes(key, rePushProperties.getSucessOnceSleepTime());
        // 结果已经为成功
        if(!isFillter){
            ReSubmitResultFunc reSubmitResultFunc = baseRePushVo.getReSubmitResultFunc();
            if(reSubmitResultFunc == null){
                Class reSubsuccessClazz = null;
                try {
                    if(!StringUtils.isEmpty(baseRePushVo.getReSubmitResultClass())){
                        reSubsuccessClazz = Class.forName( baseRePushVo.getReSubmitResultClass());
                        reSubmitResultFunc = (ReSubmitResultFunc) reSubsuccessClazz.newInstance();
                    }
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
            if(reSubmitResultFunc == null){
                log.warn("---------------未定义重复提交的返回-直接继续运行下一个");
                return true;
            }else{
                baseRePushVo.setOldResult(reSubmitResultFunc.reSubmitResult());
                return false;
            }
        }
        return isFillter;
    }
    private void rePush4Ys(RePushStrategyEntity strategyInfo,BaseRePushVo baseRePushVo) {
        Integer rePushCounts = strategyInfo.getCounts();
        final Integer sleepTime = strategyInfo.getSleepTime();
        final Integer total = rePushCounts;
        while (true) {
            boolean result = false;
            if (rePushCounts <= 0) {
                log.info(baseRePushVo.getMethodId() + "全部延时推送已完成，结果依然不好");
                //rePushFitter.removeRePush(RePushUtils.generKey(baseRePushVo));
                // 定时推送
                this.addScheduled(baseRePushVo,strategyInfo,true);
                break;
            }
            // 过滤加锁
            String key = baseRePushVo.getOnlyOnceKey();
            boolean isFillter = this.isFillter(key, baseRePushVo);
            if (!isFillter) {
                log.info(String.format("[延时]正在执行第%s次.检测到结果已经正常----过滤", total - rePushCounts + 1, result));
                this.callback(baseRePushVo, result, rePushCounts, RePushType.YS_PUSH, "重复提交-过滤");
                if (sleepTime > 0) {
                    try {
                        Thread.sleep(sleepTime);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                continue;
            }
            // 重推数据
            result = doRePushExcuteService.excute(baseRePushVo.getMethodId(), baseRePushVo.getArgs(), baseRePushVo.getIsSucessFunc(), baseRePushVo);
            log.info(String.format("[延时]策略:%s ; methodId:%s \n频率:%s毫秒/次\n总共需要推送%s次", baseRePushVo.getStrategyKey(), baseRePushVo.getMethodId(), sleepTime, total));
            log.info(String.format("[延时]正在执行第%s次.结果为:【%s】", total - rePushCounts + 1, result));
            if (result) {
                log.info(String.format("[延时]推送调用方法执行正确。无需再推；共执行了%s次", total - rePushCounts + 1));
                // 清理redis 缓存
                //rePushFitter.removeRePush(key);

            } else {
                // 移除锁
                if (baseRePushVo.getOnlyOnce()) {
                    rePushFitter.removeCheckSuccess(key);
                }
            }
            rePushCounts--;
            baseRePushVo.setRemainingCounts(rePushCounts);
            // 推送一次的回调操作
            this.callback(baseRePushVo, result, rePushCounts, RePushType.YS_PUSH, "执行完成");
            if (result) {
                break;
            }
            try {
                if (sleepTime > 0) {
                    Thread.sleep(sleepTime);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        lockMap.remove(baseRePushVo.getOnlyOnceKey());
        System.out.println("-----------------------------------所有[延时]推送已完成-----------------------------------");
    }

    /**
     * 添加任务到定时推送 保证在延时推送之后
     * @param pushVo
     * @param strategyEntity
     */
    private void addScheduled(BaseRePushVo pushVo,RePushStrategyEntity strategyEntity,boolean inYs){
        if(strategyEntity.getScheduledCounts() <= 0){
            return;
        }
        // 在延时方法里面调用
        if(inYs){
            new Thread(()->{
                rePushDbService.save2DbScheduledJob(pushVo,strategyEntity.getScheduledCounts());
            }).start();
        }
        // 非延时方法里面调用
        else{
            // 开启了延时推送，过滤。由延时进行发起定时任务
            if(strategyEntity.getCounts() <= 0){
                new Thread(()->{
                    rePushDbService.save2DbScheduledJob(pushVo,strategyEntity.getScheduledCounts());
                }).start();
            }
        }
        // 2.未定义延时推送，但是有定义及时推送 定时推送的开启必须在延时之后
    }
    /**
     * 必须有返回结果的 及时重推
     * @param baseRePushVo
     * @return
     */
    private Object rePush4Return( RePushStrategyEntity strategyInfo ,BaseRePushVo baseRePushVo) {
        Object oldResult = null;
        Integer rePushCounts = strategyInfo.getJsCounts();
        final Integer sleepTime = strategyInfo.getJsSleepTime();
        final Integer total = rePushCounts;
        log.info("-------------------[及时]开始重推---------------------");
        while (true) {
            boolean result = false;
            if (rePushCounts <= 0) {
                log.info(baseRePushVo.getMethodId() + "[及时推送]全部已完成，结果依然依然不行。是否加入定时："+strategyInfo.getScheduledCounts());
                break;
            }
            // 过滤加锁
            String key = baseRePushVo.getOnlyOnceKey();
            boolean isFittler = this.isFillter(key, baseRePushVo);
            if (!isFittler) {
                log.info(String.format("[及时推送]正在执行第%s次.检测到结果已经正常或强占中----过滤", total - rePushCounts + 1, result));
                // 推送一次的回调操作
                this.callback(baseRePushVo, result, rePushCounts, RePushType.JS_PUSH, "重复提交-过滤");
                if (sleepTime > 0) {
                    try {
                        Thread.sleep(sleepTime);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                continue;
            }
            // 重推数据
            result = doRePushExcuteService.excute(baseRePushVo.getMethodId(), baseRePushVo.getArgs(), baseRePushVo.getIsSucessFunc(), baseRePushVo);
            // 获取原来结果
            oldResult = baseRePushVo.getOldResult();
            log.info(String.format("[及时推送]策略:%s ; methodId:%s \n频率:%s毫秒/次\n总共需要推送%s次", baseRePushVo.getStrategyKey(), baseRePushVo.getMethodId(), sleepTime, total));
            log.info(String.format("[及时推送]正在执行第%s次.结果为:【%s】", total - rePushCounts + 1, result));
            if (result) {
                lockMap.remove(baseRePushVo.getOnlyOnceKey());
                log.info(String.format("[及时推送]调用方法执行正确。无需再推；共执行了%s次", total - rePushCounts + 1));
            } else {
                // 释放锁
                if (baseRePushVo.getOnlyOnce()) {
                    rePushFitter.removeCheckSuccess(key);
                }
            }
            rePushCounts--;
            baseRePushVo.setRemainingCounts(rePushCounts);
            // 推送一次的回调操作
            this.callback(baseRePushVo, result, rePushCounts, RePushType.JS_PUSH, "执行完成");
            if (result) {
                break;
            }
            try {
                if (sleepTime > 0) {
                    Thread.sleep(sleepTime);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("-----------------------------------【及时推送】所有已完成-----------------------------------");
        return oldResult;
    }

    /**
     * 回调
     * @param baseRePushVo
     * @param result
     * @param rePushCounts
     */
        private void callback(BaseRePushVo baseRePushVo, boolean result, Integer rePushCounts, RePushType pushType,String mess){
            new Thread(()->{
                rePushDbService.callbackRepush(baseRePushVo,result,rePushCounts,pushType,mess);
            }).start();
        }

}
